﻿Imports SFDL.File
Imports SFDL.LoaderExtensions
Imports SFDL.Logging
Imports System.Threading
Imports System.Threading.Tasks
Imports BrightIdeasSoftware
Imports System.Text.RegularExpressions

Module SFDLSessionHelper

    Friend _var_sfdl_session_list As New Dictionary(Of String, SFDLSession)
    Friend _obj_lock_SFDLSessionList As New Object
    Private _log As NLog.Logger = NLog.LogManager.GetLogger("SFDLSessionHelper")
    Private _log_query As NLog.Logger = NLog.LogManager.GetLogger("QueryDownloadItems")
    Private _log_session_check As NLog.Logger = NLog.LogManager.GetLogger("CheckIfSessionsComplete")

    Friend ReadOnly Property GetSFDLSessionStartTimeByKey(ByVal _session_uid As String) As Date
        Get

            Dim _rt As Date = Nothing

            If _var_sfdl_session_list.ContainsKey(_session_uid) Then
                _rt = _var_sfdl_session_list(_session_uid).TimeDownloadStarted
            End If

            Return _rt

        End Get
    End Property

    Friend ReadOnly Property GetSFDLSessionStopTimeByKey(ByVal _session_uid As String) As Date
        Get

            Dim _rt As Date = Nothing

            If _var_sfdl_session_list.ContainsKey(_session_uid) Then
                _rt = _var_sfdl_session_list(_session_uid).TimeDownloadStopped
            End If

            Return _rt

        End Get
    End Property

    Friend ReadOnly Property GetSFDLDescriptionByKey(ByVal _session_uid As String) As String
        Get

            Dim _rt As String = String.Empty

            If _var_sfdl_session_list.ContainsKey(_session_uid) Then
                _rt = _var_sfdl_session_list(_session_uid).SFDLFile.Description
            End If

            Return _rt

        End Get
    End Property

    Friend ReadOnly Property GetSFDLUploaderByKey(ByVal _session_uid As String) As String
        Get

            Dim _rt As String = String.Empty

            If _var_sfdl_session_list.ContainsKey(_session_uid) Then
                _rt = _var_sfdl_session_list(_session_uid).SFDLFile.Uploader
            End If

            Return _rt

        End Get
    End Property

    Friend ReadOnly Property GetSFDLSessionPackageListByKey(ByVal _session_uid As String) As List(Of SFDLPackage)
        Get

            Dim _rt As New List(Of SFDLPackage)

            If _var_sfdl_session_list.ContainsKey(_session_uid) Then
                _rt = _var_sfdl_session_list(_session_uid).SFDLFile.Packages
            End If

            Return _rt

        End Get
    End Property

    Friend ReadOnly Property GetSFDLSessionCheckedItemsByKey(ByVal _session_uid As String) As List(Of OLV_Item)
        Get

            Dim _rt As New List(Of OLV_Item)

            If _var_sfdl_session_list.ContainsKey(_session_uid) Then

                _rt.AddRange(From _item In _var_sfdl_session_list(_session_uid).OLVItemList Where _item.Checked = True)

            End If

            Return _rt

        End Get
    End Property

    Friend ReadOnly Property GetSDFLSessionList As Dictionary(Of String, SFDLSession)
        Get
            Return _var_sfdl_session_list
        End Get
    End Property

    Friend ReadOnly Property GetSFDLSessionByKey(ByVal _key As String) As SFDLSession
        Get
            If _var_sfdl_session_list.ContainsKey(_key) Then
                Return _var_sfdl_session_list.Item(_key)
            Else
                Return Nothing
            End If
        End Get
    End Property

    Friend ReadOnly Property GetSFDLSessionList As Dictionary(Of String, SFDLSession)
        Get
            Return _var_sfdl_session_list
        End Get
    End Property

    Friend Sub AddSFDLSession(ByVal _session As KeyValuePair(Of String, SFDLSession))

        Try

            Monitor.Enter(_obj_lock_SFDLSessionList)

            _var_sfdl_session_list.Add(_session.Key, _session.Value)

        Catch ex As Exception
            _log.Error(ex, "Konnte SFDL Session nicht hinzufügen!")
        Finally
            Monitor.Exit(_obj_lock_SFDLSessionList)
        End Try

    End Sub

    Friend Sub UpdateSFDLSession(ByVal _session As KeyValuePair(Of String, SFDLSession))

        Try

            Monitor.Enter(_obj_lock_SFDLSessionList)

            If _var_sfdl_session_list.ContainsKey(_session.Key) Then

                _var_sfdl_session_list.Remove(_session.Key)
                _var_sfdl_session_list.Add(_session.Key, _session.Value)

            End If

        Catch ex As Exception
            _log.Error(ex, "Konnte SFDL Session nicht updaten!")
        Finally
            Monitor.Exit(_obj_lock_SFDLSessionList)
        End Try

    End Sub

    Friend Sub SetSFDLSessionGroupState(ByVal _session_name As String, ByVal _group_state As SFDLSessionEnums.GroupState)

        Try

            Monitor.Enter(_obj_lock_SFDLSessionList)

            Select Case _group_state

                Case SFDLSessionEnums.GroupState.Collapsed

                    _var_sfdl_session_list.Item(_session_name).GroupState = SFDLSessionEnums.GroupState.Collapsed

                Case SFDLSessionEnums.GroupState.Expanded

                    _var_sfdl_session_list.Item(_session_name).GroupState = SFDLSessionEnums.GroupState.Expanded

            End Select

        Catch ex As Exception
            _log.Error(ex, ex.Message)
        Finally
            Monitor.Exit(_obj_lock_SFDLSessionList)
        End Try

    End Sub

    Friend Sub SetSFDLSessionState(ByVal _session_name As String, ByVal _state As SFDLSessionEnums.SFDLSessionState)

        Try

            Monitor.Enter(_obj_lock_SFDLSessionList)

            If _var_sfdl_session_list.ContainsKey(_session_name) Then

                Select Case _state

                    Case SFDLSessionEnums.SFDLSessionState.DonwloadStopped

                        _var_sfdl_session_list.Item(_session_name).SessionState = SFDLSessionEnums.SFDLSessionState.DonwloadStopped

                    Case SFDLSessionEnums.SFDLSessionState.DownloadComplete

                        _var_sfdl_session_list.Item(_session_name).SessionState = SFDLSessionEnums.SFDLSessionState.DownloadComplete

                    Case SFDLSessionEnums.SFDLSessionState.DownloadRunning

                        _var_sfdl_session_list.Item(_session_name).SessionState = SFDLSessionEnums.SFDLSessionState.DownloadRunning

                    Case SFDLSessionEnums.SFDLSessionState.Queued

                        _var_sfdl_session_list.Item(_session_name).SessionState = SFDLSessionEnums.SFDLSessionState.Queued

                End Select

            End If

        Catch ex As Exception
            _log.Error(ex, ex.Message)
        Finally
            Monitor.Exit(_obj_lock_SFDLSessionList)
        End Try

    End Sub

    Friend Sub RemoveSFDLSessionByKey(ByVal _session_key As String)

        Try

            Monitor.Enter(_obj_lock_SFDLSessionList)

            If _var_sfdl_session_list.ContainsKey(_session_key) Then

                _var_sfdl_session_list.Remove(_session_key)

            End If

        Catch ex As Exception
            _log.Error(ex, "Konnte SFDL Session nicht entfernen!")
        Finally
            Monitor.Exit(_obj_lock_SFDLSessionList)
        End Try

    End Sub

    Friend Sub SyncSFDLSessionwithGUI(Optional ByVal _rebuild_index As Boolean = False)

        Task.Factory.StartNew(Sub()

                                  Try

                                      Monitor.Enter(_obj_lock_SFDLSessionList)

                                      Dim _new_list As New Dictionary(Of String, SFDLSession)

                                      For Each _group As OLVGroup In frm_main.OLV_Download_Items.OLVGroups

                                          Dim _session As SFDLSession = GetSFDLSessionByKey(_group.Name)

                                          If _rebuild_index = True Then

                                              For Each _item As OLV_Item In _session.OLVItemList

                                                  _item.GroupSortIndicator = _new_list.Count + 1 & ";" & _item.PackageName & ";" & _session.SFDLSessionName
                                              Next

                                          End If

                                          If Not _new_list.ContainsKey(_session.SFDLSessionName) Then
                                              _new_list.Add(_session.SFDLSessionName, _session)
                                          End If

                                      Next

                                      _var_sfdl_session_list = _new_list

                                      If _rebuild_index = True Then

                                          frm_main.OLV_Download_Items.ClearObjects()

                                          For Each _session_obj In _var_sfdl_session_list
                                              frm_main.OLV_Download_Items.AddObjects(_session_obj.Value.OLVItemList)
                                          Next

                                      End If

                                  Catch ex As Exception
                                      _log.Error(ex, ex.Message)
                                  Finally
                                      Monitor.Exit(_obj_lock_SFDLSessionList)
                                  End Try

                              End Sub, New CancellationToken, TaskCreationOptions.None, GetRootUIContext)

    End Sub

    Friend Sub IncreaseActiveThreadCounter(ByVal _session_key As String)

        Try

            Monitor.Enter(_obj_lock_SFDLSessionList)

            If _var_sfdl_session_list.ContainsKey(_session_key) Then
                _var_sfdl_session_list(_session_key).ActiveThreads += 1
            End If

        Catch ex As Exception
            _log.Error(ex, "Konnte Active Thread Counter nicht erhöhen!")
        Finally
            Monitor.Exit(_obj_lock_SFDLSessionList)
        End Try

    End Sub

    Friend Sub DecreaseActiveThreadCounter(ByVal _session_key As String)
        Try

            Monitor.Enter(_obj_lock_SFDLSessionList)

            If _var_sfdl_session_list.ContainsKey(_session_key) Then
                _var_sfdl_session_list(_session_key).ActiveThreads -= 1
            End If

        Catch ex As Exception
            _log.Error(ex, "Konnte Active Thread Counter nicht verringern!")
        Finally
            Monitor.Exit(_obj_lock_SFDLSessionList)
        End Try

    End Sub

    Friend Sub ResetActiveThreadCounter(ByVal _session_key As String)

        Try

            Monitor.Enter(_obj_lock_SFDLSessionList)

            If _var_sfdl_session_list.ContainsKey(_session_key) Then
                _var_sfdl_session_list(_session_key).ActiveThreads = 0
            End If

        Catch ex As Exception
            _log.Error(ex, "Konnte Active Thread Counter nicht zurücksetzten!")
        Finally
            Monitor.Exit(_obj_lock_SFDLSessionList)
        End Try

    End Sub

    Friend Function QueryDownloadItems(ByVal _current_thread_count As Integer) As List(Of OLV_Item)

        Dim _rt_download_items As New List(Of OLV_Item)
        Dim _settings As Settings = GetSettings

        Try
            Monitor.Enter(_obj_lock_SFDLSessionList)

            If GetDownloadStopInProgress = True Then
                Throw New DownloadCompletedException("Download wird gerade gestoppt!")
            End If

            If _current_thread_count = _settings.MaxDownloadThreads Then
                _log_query.Info("Maximale Thread Anzahl ist bereits erreicht! (Loader Limit)")
                Throw New ThreadLoaderLimitReached
            End If

            For Each _sfdl_session_item In GetSDFLSessionList

                Dim _var_sfdl_session As New SFDLSession

                If GetDownloadStopInProgress = True Then
                    Throw New DownloadCompletedException("Download wird gerade gestoppt!")
                End If

                If _current_thread_count = _settings.MaxDownloadThreads Then
                    Throw New ThreadLoaderLimitReached("Maximale Thread Anzahl ist erreicht! (Loader Limit)")
                End If

                Try

                    _var_sfdl_session = _sfdl_session_item.Value

                    If _var_sfdl_session.ActiveThreads = _var_sfdl_session.SFDLFile.MaxDownloadThreads Then
                        Throw New ThreadSFDLFileLimitReached("Maximale Anzahl ist erreicht! (SFDL File Limit)")
                    End If

                    If _settings.InstantVideo = True Then 'Nur wenn in Einstellungen aktiviert

                        For Each _item As OLV_Item In _var_sfdl_session.OLVItemList.Where(Function(_my_item As OLV_Item) _my_item.Checked = True And _my_item.RequiredForInstantVideo = True)

                            Select Case _item.Status

                                Case Status.Waiting

                                    _item.Status = Status.Running
                                    _rt_download_items.Add(_item)
                                    _current_thread_count += 1
                                    _var_sfdl_session.ActiveThreads += 1
                                    _log_query.Info("Thread Counter um 1 erhöt!")

                            End Select


                            If _var_sfdl_session.ActiveThreads = _var_sfdl_session.SFDLFile.MaxDownloadThreads Then
                                Throw New ThreadSFDLFileLimitReached("Maximale Anzahl ist erreicht! (SFDL File Limit)")
                            End If

                            If _current_thread_count = _settings.MaxDownloadThreads Then
                                Throw New ThreadLoaderLimitReached("Maximale Thread Anzahl ist erreicht! (Loader Limit)")
                            End If

                        Next

                    End If

                    For Each _item As OLV_Item In _var_sfdl_session.OLVItemList.Where(Function(_my_item As OLV_Item) _my_item.Checked = True)

                        _log_query.Debug("Item Name: {0}", _item.FileName)
                        _log_query.Debug("Item Status: {0}", _item.Status.ToString)

                        Select Case _item.Status

                            Case Status.Waiting

                                _item.Status = Status.Running
                                _rt_download_items.Add(_item)
                                _current_thread_count += 1
                                _var_sfdl_session.ActiveThreads += 1
                                _log_query.Info("Thread Counter um 1 erhöt!")

                        End Select

                        If _var_sfdl_session.ActiveThreads = _var_sfdl_session.SFDLFile.MaxDownloadThreads Then
                            Throw New ThreadSFDLFileLimitReached("Maximale Anzahl ist erreicht! (SFDL File Limit)")
                        End If

                        If _current_thread_count = _settings.MaxDownloadThreads Then
                            Throw New ThreadLoaderLimitReached("Maximale Thread Anzahl ist erreicht! (Loader Limit)")
                        End If

                    Next

                Catch ex As ThreadSFDLFileLimitReached
                    _log_query.Info(ex, ex.Message)
                Finally

                    If _var_sfdl_session.SessionState = SFDLSessionEnums.SFDLSessionState.Queued And _var_sfdl_session.ActiveThreads > 0 Then
                        _var_sfdl_session.TimeDownloadStarted = DateTime.Now
                        _var_sfdl_session.SessionState = SFDLSessionEnums.SFDLSessionState.DownloadRunning
                    End If

                End Try

            Next

        Catch ex As DownloadCompletedException
            _log_query.Info(ex, ex.Message)

        Catch ex As ThreadLoaderLimitReached
            _log_query.Info(ex, ex.Message)

        Catch ex As Exception
            _log_query.Error(ex, ex.Message)

        Finally
            Monitor.Exit(_obj_lock_SFDLSessionList)
        End Try

        Return _rt_download_items

    End Function

    Friend Sub ParseUnRARChains()

        Try

            Monitor.Enter(_obj_lock_SFDLSessionList)

            System.Threading.Tasks.Parallel.ForEach(_var_sfdl_session_list, Sub(_sessionitem)

                                                                                Dim _sfdl_session As SFDLSession

                                                                                _sfdl_session = _sessionitem.Value

                                                                                _sfdl_session.UnRarChains.Clear()

                                                                                For Each _item As OLV_Item In _sfdl_session.OLVItemList.Where(Function(_my_item As OLV_Item) IO.Path.GetExtension(_my_item.FileName).Equals(".rar"))

                                                                                    Dim _unrarchain As New UnRARChain
                                                                                    Dim _searchpattern As Regex
                                                                                    Dim _count As Integer

                                                                                    If Not _item.FileName.Contains(".part") Then

                                                                                        _count = 0

                                                                                        _item.FirstUnRarFile = True
                                                                                        _item.RequiredForInstantVideo = True
                                                                                        _unrarchain.MasterUnRarChainFile = _item

                                                                                        _log.Debug("First UnRar File: {0}", _item.FileName)

                                                                                        _searchpattern = New Regex("filename\.r[0-9]{1,2}".Replace("filename", IO.Path.GetFileNameWithoutExtension(_item.FileName)))

                                                                                        For Each _chainitem As OLV_Item In _sfdl_session.OLVItemList.Where(Function(_my_item As OLV_Item) _searchpattern.IsMatch(_my_item.FileName))

                                                                                            _log.Debug("ChainItem FileName: {0}", _chainitem.FileName)

                                                                                            If _count < 1 Then
                                                                                                _chainitem.RequiredForInstantVideo = True
                                                                                            End If

                                                                                            _unrarchain.ChainMemberFiles.Add(_chainitem)

                                                                                            _count += 1

                                                                                        Next

                                                                                        _sfdl_session.UnRarChains.Add(_unrarchain)

                                                                                    Else

                                                                                        _count = 0

                                                                                        If _item.FileName.EndsWith(".part1.rar") Then 'MasterFile

                                                                                            Dim _tmp_filename_replace As String

                                                                                            _log.Debug("First UnRar File: {0}", _item.FileName)
                                                                                            _item.FirstUnRarFile = True
                                                                                            _item.RequiredForInstantVideo = True
                                                                                            _unrarchain.MasterUnRarChainFile = _item

                                                                                            _tmp_filename_replace = IO.Path.GetFileNameWithoutExtension(_item.FileName).Replace(".part1", "")

                                                                                            _searchpattern = New Regex("filename\.part[0-9]{1,3}.rar".Replace("filename", _tmp_filename_replace))

                                                                                            For Each _chainitem As OLV_Item In _sfdl_session.OLVItemList.Where(Function(_my_item As OLV_Item) _searchpattern.IsMatch(_my_item.FileName) And Not _my_item.FileName.Equals(_unrarchain.MasterUnRarChainFile.FileName))

                                                                                                _log.Debug("ChainItem FileName: {0}", _chainitem.FileName)

                                                                                                If _count < 1 Then
                                                                                                    _chainitem.RequiredForInstantVideo = True
                                                                                                End If

                                                                                                _unrarchain.ChainMemberFiles.Add(_chainitem)

                                                                                                _count += 1

                                                                                            Next

                                                                                            _sfdl_session.UnRarChains.Add(_unrarchain)

                                                                                        End If


                                                                                    End If

                                                                                Next

                                                                            End Sub)




        Catch ex As Exception
        Finally
            Monitor.Exit(_obj_lock_SFDLSessionList)
        End Try

    End Sub

    Friend Function isSessionComplete(ByVal _sfdl_session As SFDLSession) As Boolean

        Dim _session_complete As Boolean = True 'Sobald ein Item nicht fetig ist oä, wird der Wert auf false gesetzet
        Dim _session_downloaded_file As Boolean = False

        Try

            _log.Info("Prüfe Session {0}", _sfdl_session.SFDLSessionName)

            If _sfdl_session.SessionState = SFDLSessionEnums.SFDLSessionState.DownloadComplete Then
                Throw New DownloadCompletedException("Session muss nicht geprüft werden da Sie schon fertig ist!")
            End If

            If GetDownloadStopInProgress = True Then
                Throw New Exception("Download wird gestoppt!")
            End If


            For Each _item As OLV_Item In _sfdl_session.OLVItemList.Where(Function(_my_item As OLV_Item) _my_item.Checked = True)

                _log.Debug("Item FileName: {0}", _item.FileName)
                _log.Debug("Item Status: {0}", _item.Status.ToString)

                Select Case _item.Status

                    Case Status.Complete

                    Case Status.CompleteHashInvalid

                    Case Status.CompleteHashValid

                    Case Else

                        _session_complete = False

                End Select

            Next

            If _session_complete = True Then

                _sfdl_session.SessionState = SFDLSessionEnums.SFDLSessionState.DownloadComplete

                _sfdl_session.TimeDownloadStopped = Now

                _log.Info("Session {0} ist fertig heruntergeladen!", _sfdl_session.SFDLSessionName)

            Else
                _sfdl_session.SessionState = SFDLSessionEnums.SFDLSessionState.DownloadRunning

                _log.Info("Session {0} ist NICHT fertig heruntergeladen!", _sfdl_session.SFDLSessionName)

            End If

        Catch ex As DownloadCompletedException
            _log.Error(ex, ex.Message)
        End Try

        Return _session_complete

    End Function

    Friend Function CheckIfDownloadComplete() As CheckIfDownloadCompleteResult

        Dim _download_complete As Boolean = True
        Dim _result As New CheckIfDownloadCompleteResult

        Try

            Monitor.Enter(_obj_lock_SFDLSessionList)

            _log_session_check.Info("Prüfe ob Sessions fertig sind...")


            For Each _sessionitem In _var_sfdl_session_list

                Dim _session_complete As Boolean = True 'Sobald ein Item nicht fetig ist oä, wird der Wert auf false gesetzet
                Dim _session_downloaded_file As Boolean = False
                Dim _sfdl_session As SFDLSession
                Dim _only_qued As Boolean = False

                Try

                    _sfdl_session = _sessionitem.Value

                    _log_session_check.Info("Prüfe Session {0}", _sfdl_session.SFDLSessionName)

                    If _sfdl_session.SessionState = SFDLSessionEnums.SFDLSessionState.DownloadComplete Then
                        Throw New DownloadCompletedException("Session muss nicht geprüft werden da Sie schon fertig ist!")
                    End If

                    If GetDownloadStopInProgress = True Then
                        Throw New Exception("Download wird gestoppt!")
                    End If

                    If _sfdl_session.OLVItemList.Where(Function(_my_item As OLV_Item) _my_item.Checked = True).Count = 0 Then
                        _session_complete = True
                        _only_qued = True
                    End If

                    If _session_complete = True Then

                        If _only_qued = True Then
                            _sfdl_session.SessionState = SFDLSessionEnums.SFDLSessionState.DownloadCompleteButOnlyQueued
                        Else
                            _sfdl_session.SessionState = SFDLSessionEnums.SFDLSessionState.DownloadComplete
                        End If

                        _sfdl_session.TimeDownloadStopped = Now

                        'ToDo: Evtl. Post SessionDownloadEvent für PLugins wieder aufnehmen

                        'Debug.WriteLine(DateDiff(DateInterval.Second, Now, _sfdl_session.TimeDownloadStopped))

                        'System.Threading.Tasks.Parallel.ForEach(_plugin_connector.Plugins, Sub(_plugin As SFDL.PluginInterface.PluginInterface.SFDLLoaderPlugin)

                        '                                                                       If _plugin.SpecialPluginFunctions.Contains(PluginInterface.PluginInterface.SpeicalPluginFunction.PostSessionDownload) Then

                        '                                                                           Dim _thread As New System.Threading.Thread(Sub() _plugin.PostSessionDownload(_sfdl_session, _session_downloaded_file))

                        '                                                                           _thread.Start()

                        '                                                                       End If

                        '                                                                   End Sub)

                        _log_session_check.Info("Session {0} ist fertig heruntergeladen", _sfdl_session.SFDLSessionName)

                        _result.CompleteSessionIDs.Add(_sfdl_session.SFDLSessionName)

                    Else
                        _sfdl_session.SessionState = SFDLSessionEnums.SFDLSessionState.DownloadRunning

                        _log_session_check.Info("Session {0} ist NICHT fertig heruntergeladen", _sfdl_session.SFDLSessionName)
                        _download_complete = False

                    End If

                Catch ex As DownloadCompletedException
                    _log_session_check.Info(ex, ex.Message)

                End Try

            Next

        Catch ex As Exception
            _log.Error(ex, ex.Message)
        Finally
            _log_session_check.Info("Prüfen der Sessions abgeschlossen!")
            Monitor.Exit(_obj_lock_SFDLSessionList)
        End Try

        _result.AllSessionsComplete = _download_complete

        Return _result

    End Function

End Module